# -*- coding: utf-8 -*-
"""
    The following copyright and license notice apply only to:
        - those parts of this Turbogears 2 project which are not
          part of the stock quickstarted project
        - those parts which are not otherwise licensed otherwise

    Copyright (C) 2009 Ciaran Farrell <cfarrell1980@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from tg import expose, flash, require, url, request, response, redirect,tmpl_context,validate, config
identity = request.environ.get('repoze.who.identity')
from tg.controllers import TGController
from pylons.i18n import ugettext as _, lazy_ugettext as l_
try:
    from pylons.decorators.cache import beaker_cache
except ImportError:
    print "fix importing beaker_cache decorator"
#from catwalk.tg2 import Catwalk
from repoze.what import predicates
from formencode import validators,Schema
from lifepyscle.lib.base import BaseController
from lifepyscle.lib.uploader import * # provides functionality to save uploaded files
from lifepyscle.lib.coversheet import * # provides coversheet generation
from webhelpers import paginate
from lifepyscle.model import DBSession, metadata
from lifepyscle.controllers.error import ErrorController
from lifepyscle import model
from lifepyscle.model import *
from lifepyscle.controllers.secure import SecureController
import tw.forms as twf, tw.dynforms as twd
from lifepyscle.controllers.party import parties
from lifepyscle.widgets import add_metadata,quick_autocomplete,add_event
from lifepyscle.widgets.fancybox import FancyBox
from lifepyscle.widgets import term
import os,shutil,pylons

from repoze.what import predicates

cat="record" # added to every returned dict
default_ipp = config.get('default_pagination_items_per_page')

event_form = add_event.event_form
term_form = term.term_form
groups = DBSession.query(Group).all()
group_opts = []
for x in groups:
    group_opts.append((x.group_id,x.group_name))

def getParties():
    p = DBSession.query(Party.party_id,Party.name).all()
    return p

def getPartyOptions():
    party_options = []
    for p in getParties():
        party_options.append((p.party_id,p.name))
    return party_options

def eventTemplate(record):
    """ Given a record, construct a genshi template with the appropriate events """
    events=""
    event_ids = [x.event_id for x in record.events]
    all_e = DBSession.query(Event).filter(Event.event_id.in_(event_ids)).order_by(Event.starts).all()
    for e in all_e:
        events = events+"""<div class='grid_9'><div class="grid_1 alpha"><a href="/event/view/%s"><img src="/images/edit.png" alt="Edit Event" style="border:0px;"/></a><a href="/event/delete/%d"><img src="/images/delete.png" alt="Delete Event" style="border:0px;"/></a><a class="iframe" href="/reminder/iframe/%s"><img src="/images/chronometer.png" alt="Add Reminder" style="border:0px;"/></a></div><div class="grid_2">%s</div><div class="grid_2">%s</div><div class="grid_4 omega">%s</div></div>"""%(e.event_id,e.event_id,e.event_id,e.fmtOccurs,e.title,e.description)
    return """<div xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" class="grid_9">%s</div>"""%(events)

def ean13():
    # get a random 13 digit key - this will be the key used to create
    # a ean13 barcode
    from random import random,randint
    key = str(random())[2:]
    key = "%s%s"%(key,str(randint(1,10))) # round to 13
    return key

add_metadata_form = add_metadata.add_metadata_form

class RecordController(BaseController):

    allow_only = predicates.not_anonymous(msg=l_(u'Only authorised users may view records'))

    @expose('lifepyscle.templates.test_autoc')
    def entry(self, **kw):
        tmpl_context.parties_autocomplete = quick_autocomplete.parties_autocomplete_form
        tmpl_context.records_autocomplete = quick_autocomplete.records_autocomplete_form
        return dict(value=kw,cat=cat)

    @expose('json')
    def fetch_records(self):
        # this is a fairly stupid function. It grabs all records and runtime and doesnt
        # do any more lookups. Thus, it is difficult to set an id in a HiddenField
        records = DBSession.query(Record.title).all()
        return dict(data=records)

    @expose('json')
    def fetch_parties(self):
        parties = DBSession.query(Party.name).all()
        return dict(data=parties)

    @expose('lifepyscle.templates.record')
    #@require(predicates.in_group('cman', msg=l_('Only for contract managers')))
    def index(self,**kw):
        #return dict(page="record",cat=cat)
        requested_url = request.environ.get('PATH_INFO')
        if request.environ.get('QUERY_STRING'):
            requested_url += '?' + request.environ['QUERY_STRING']
        print requested_url
        redirect(url('/record/list'))

    @expose('lifepyscle.templates.add_record')
    #@require(predicates.in_group('cman', msg=l_('Only for contract managers')))
    def add(self,**kw):
        # TODO: load this users preferences from file
        tmpl_context.add_metadata_form = add_metadata_form
        party_options = []
        for p in parties():
            party_options.append((p.party_id,p.name))
        return dict(page="Add Record",groups=groups,parties=parties(),cat=cat,party_options=party_options,)

    @expose()
    #@require(predicates.in_group('cman', msg=l_('Only for contract managers')))
    @validate(form=add_metadata_form,error_handler=add)
    def do_add(self,**kw):
        this_user = DBSession.query(User).filter(User.user_name==u'%s'%identity['repoze.who.userid']).one()
        from lifepyscle.lib import ean13 as barcode
        bcode = barcode
        rec = Record()
        rec.ean13 =bcode.ean13()
        rec.title = kw['title']
        rec.description = kw['description']
        this_user.records.append(rec)
        parties = DBSession.query(Party)
        for x in kw['parties_involved']:
            rec.parties.append(parties.get(x))
        id = rec.record_id
        flash("Successfully added the new record","ok")
        redirect(url('/record/view/%s'%str(id)))


    @expose('json')
    def add_record(self,**kw):
        print kw['party_name']
        return dict(party_id="55",party_name=kw['party_name'])


    @validate(validators={"page":validators.Int(), "rp":validators.Int()})
    @expose('json')
    def fetch(self, page=1, rp=25, sortname='title', sortorder='asc', qtype=None, query=None):
        try:
            offset = (int(page)-1) * int(rp)
        except:
            page = 1
            rp=25
            offset = (int(page)-1) * int(rp)
        if (query):
            # note: the qtype must be cast to a str. as unicode it causes an error. query can be unicode
            d = {str(qtype):query}
            records = DBSession.query(Record).filter_by(**d)
        else:
            records = DBSession.query(Record)
        total = records.count()
        column = getattr(Record, sortname)
        records = records.order_by(getattr(column,sortorder)()).offset(offset).limit(rp)
        rows = [{'id'  : record.record_id,
                    'cell': [record.record_id, record.title]} for record in records]
        return dict(page=page, total=total, rows=rows)

    @expose('json')
    def update(self,id,value,**kw):
        # over the id param we get something like title_5 whereby what comes before the
        # underscore is the dbfield and what comes after is the record id
        f_r = id.split("_")
        try:
            field,record_id = f_r[0],f_r[1]
        except:
            print f_r
            return "busted"
        else:
            try:
                rec = DBSession.query(Record).filter(Record.record_id==record_id).one()
            except:
                return "record doesn't exist"
            else:
                if field.lower() == 'title':
                    rec.title = value
                    return value
                elif field.lower() == 'description':
                    rec.description = value
                    return value
                else:
                    return "dont know that field"

    @expose()
    def update_parties(self,record_id,**kw):
        """ id is record id, kw['parties_involved'] are the parties associated
            kw['parties_involved'] looks like this: [u'1',u'2'] 
        """ 
        try:
            R = DBSession.query(Record).filter(Record.record_id == record_id).one()
        except:
            # log this.
            flash("No idea what record you want to change - bug","error")
            redirect(url('/record/view/%s#parties'%record_id))
        else:
            old_parties = R.parties
            try:
                new_parties = DBSession.query(Party).filter(Party.party_id.in_([int(x) for x in kw['parties_involved']])).all()
            except Exception,e:
                print e
            else:
                R.parties = new_parties
            flash("Parties successfully updated","ok")
            redirect(url('/record/view/%s#parties'%record_id))

    @expose('lifepyscle.templates.list_record')
    #@beaker_cache(expire=300,type='memory',query_args=True) #causing a strange error
    def list(self,**kw):
        ipp=default_ipp # saved in app_cfg
        page=1 # a default which is changed by GET params
        if kw.has_key('ipp'): # see if the user set items_per_page himself
            if kw['ipp'].isdigit():
                ipp=int(kw['ipp'])
        if kw.has_key('page'): # see if the user set items_per_page himself
            if kw['page'].isdigit():
                page=int(kw['page'])
        tmpl_context.find_record_form = quick_autocomplete.records_autocomplete_form
        # perfect opportunity to filter/join to get only records for certain party
        filtering_parties = [] # only records from these parties
        if kw.has_key('pty'):
            # this will either be a unicode str if only one sent or a list if multiple sent
            if isinstance(kw['pty'],list):
                for element in kw['pty']:
                    if element.isdigit():
                        if int(element) not in filtering_parties:
                            filtering_parties.append(int(element))
            elif isinstance(kw['pty'],unicode): # unicode gets passed, not str
                if kw['pty'].isdigit():
                    if int(kw['pty']) not in filtering_parties:
                        filtering_parties.append(int(kw['pty']))
        if len(filtering_parties):
            p = DBSession.query(Party).filter(Party.party_id.in_(filtering_parties)).all()
            records = []
            for x in p:
                for rec in x.records:
                    if rec not in records:
                        records.append(rec)
        else:
            records = DBSession.query(Record).all()
        total = len(records)
        tmpl_context.page = paginate.Page(records,page=page,items_per_page=ipp,item_count=total)
        tmpl_context.gah = tmpl_context.page.pager('$link_previous ~3~ $link_next (Page $page of $page_count)',ipp=ipp)
        return dict(value=kw,cat=cat)

    @expose()
    def by_id(self,id=None,**kw):
        """ Redirect to /record/view/id """
        if not kw['records_autocomplete']:
            flash("No record title provided","warning")
            u = '/record/list'
        else:
            try:
                R = DBSession.query(Record.record_id).filter(Record.title=='%s'%kw['records_autocomplete']).one()
            except Exception,e:
                flash("Failed to retrieve that record: %s"%str(e),"error")
                u = '/record/list'
            else:
                u = '/record/view/%s'%str(R.record_id)
        redirect(url(u))

    @expose('lifepyscle.templates.test_autoc')
    def test(self):
        from lifepyscle.widgets.flowtabs import Tabs
        tmpl_context.tabs = Tabs()
        return dict(cat=cat)

    @expose('lifepyscle.templates.view_record')
    def view(self,id=None,**kw):
        form_id = "add_event_form"
        fieldset_id = "AddEventFieldSet" # nasty hack to get the tw generated fieldset id
        from lifepyscle.widgets.collection import update_parties_form
        from lifepyscle.widgets.event import event_form
        from lifepyscle.widgets.dummy import dummy_dynamicform,dummy_multipleupload
        from lifepyscle.widgets.jeditable import Jeditable
        from lifepyscle.widgets.flowtabs import Tabs
        tmpl_context.update_parties_form = update_parties_form
        #tmpl_context.add_event_form = add_event_form
        #tmpl_context.update_title_form = update_title_form
        tmpl_context.dummy_tabs = Tabs()
        tmpl_context.dummy_jeditable = Jeditable(action="/record/update/")
        tmpl_context.dummy_dynamicform = dummy_dynamicform
        tmpl_context.event_form = event_form
        tmpl_context.dummy_multipleupload = dummy_multipleupload
        if not id:
            flash("No particular record requested. Defaulting to showing a list of available records","info")
            redirect(url('/record/list'))
        elif not id.isdigit():
            flash("Wrong type passed. The parameter id should be an integer. The value passed was %s"%id,"error")
            redirect(url('/record/list'))
        else:
            try:
                record = DBSession.query(Record).filter(Record.record_id==id).one()
            except:
                flash("No record found matching id %d"%int(id),"alert")
                redirect(url('/record/list'))
            else:
                tmpl_context.fb = FancyBox()
                party_options = getPartyOptions()
                selected,selected_parties = {},record.parties
                event_template = eventTemplate(record)
                # NB: pass the selected options as a dict - e.g. {'1':'selected','4':selected'}
                for x in selected_parties:
                    selected[x.party_id] = 'selected'
                tmpl_context.term_form = term_form
                return dict(redir_url='/record/view/%d#events'%record.record_id,record=record,cat=cat,party_options=party_options,selected=selected,form_id=form_id,fieldset_id=fieldset_id,event_template=event_template)

    @expose('json')
    @validate(form=event_form,error_handler=view)
    def remote_add_event(self,**kw):
        record_id = int(kw['record_id'])
        events = ""
        try:
            R = DBSession.query(Record).filter(Record.record_id==record_id).one()
        except Exception,e:
            return events
        else:
            new_event = Event()
            new_event.title = kw['title']
            new_event.description = kw['description']
            new_event.type = kw['type']
            new_event.occurs = kw['occurs']
            R.events.append(new_event)
            return eventTemplate(R)

    @expose('json')
    def remote_remove_event(self,**kw):
        record_id = int(kw['record_id'])
        event_id = int(kw['event_id'])
        events = ""
        try:
            R = DBSession.query(Record).filter(Record.record_id==record_id).one()
        except Exception,e:
            return events
        else:
            R.events.remove(new_event)
            return eventTemplate(R)

    @expose('json')
    def fetch_types(self):
        types = DBSession.query(Event.type).distinct().all()
        return dict(data=types)

    @expose()
    def coversheet(self,record_id,**kw):
        """
        Generate a coversheet for a record and force download of the coversheet
        """
        R = DBSession.query(Record).filter(Record.record_id==record_id).one()
        pdfname = "%s.pdf"%R.ean13 # improve this to make it someway more meaningful
        ean13 = str(R.ean13)
        infodict={}
        infodict['title'] = R.title
        infodict['description'] = R.description
        infodict['events'] = R.events
        try:
            coversheet = genCoverSheet(R.ean13,pdfname,**infodict)
        except Exception,e:
            raise
        else:
            file_to_serve = open(coversheet,'rb')
            transient = file_to_serve.read()
            file_to_serve.close()
            # delete the coversheet now, or schedule it for deletion e.g. with cron
            response.headers['Content-Disposition'] = 'attachment; filename="%s"'%(pdfname)
            response.headers['Content-Length'] = len(transient) # ie6 over ssl workaround
            response.headers['Pragma'] =  'public'  # ie6 over ssl workaround
            response.headers['Cache-Control'] = 'max-age=0'  # ie6 over ssl workaround
            return transient

